package com.nulldev.util.web.YouTubeExtractor.util;

import static com.nulldev.util.web.YouTubeExtractor.util.YouTubeExtractorUtil.YT_DEFAULT_LANG_CODE;
import static com.nulldev.util.web.YouTubeExtractor.util.YouTubeExtractorUtil.makeDummyReducedCPN;

import java.util.Map;

import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.web.URLUtils.URLUtils;
import com.nulldev.util.web.YouTubeExtractor.YouTubeExtractorConstants;
import com.nulldev.util.web.YouTubeExtractor.baltimore.BaltimoreUserAgent;
import com.nulldev.util.web.YouTubeExtractor.extractors.util.YTIAPIClientType;
import com.nulldev.util.web.YouTubeExtractor.extractors.util.YTIAPIClientType.YTIAPIClientEnums;

public interface YTRequestObject {

	public static final String YT_REQUEST_VIDEO_ID = "-yt-req-video-id";
	public static final Class<?> YT_REQUEST_VIDEO_ID_TYPE = String.class;

	public static final String YT_REQUEST_CLICK_TRACKING_PARAMS = "-yt-req-click-tracking-params";
	public static final Class<?> YT_REQUEST_CLICK_TRACKING_PARAMS_TYPE = String.class;

	public static final String YT_REQUEST_CLIENT_TYPE = "-yt-req-client-type";
	public static final Class<?> YT_REQUEST_CLIENT_TYPE_TYPE = YTIAPIClientType.class;

	public static final String YT_REQUEST_USER_AGENT = "-yt-req-user-agent";
	public static final Class<?> YT_REQUEST_USER_AGENT_TYPE = String.class;

	public static final String YT_REQUEST_CLIENT_VERSION = "-yt-req-client-version";
	public static final Class<?> YT_REQUEST_CLIENT_VERSION_TYPE = String.class;

	public static final String YT_ENABLE_PLAYBACK_CONTEXT = "-yt-req-playback-context";
	public static final Class<?> YT_ENABLE_PLAYBACK_CONTEXT_TYPE = boolean.class;

	public static final String YT_ENABLE_FALLEN_BACK_MODE = "-yt-req-fallen-back";
	public static final Class<?> YT_ENABLE_FALLEN_BACK_MODE_TYPE = boolean.class;

	public static final String YT_ENABLE_PLAYER_TYPE = "-yt-enable-player-type";
	public static final Class<?> YT_ENABLE_PLAYER_TYPE_TYPE = boolean.class;

	public static final String YT_REQUEST_NO_BROWSER_VERSION = "-yt-req-no-browser-version";
	public static final Class<?> YT_REQUEST_NO_BROWSER_VERSION_TYPE = boolean.class;

	public static final String YT_REQUEST_CUSTOM_PLATFORM = "-yt-req-custom-platform";
	public static final Class<?> YT_REQUEST_CUSTOM_PLATFORM_TYPE = String.class;

	/* Check Map */

	public static final Map<String, Class<?>> ARGUMENT_CLASSES = FastMaps.fixedUniMap(new Object[][]
		{
				{ YT_REQUEST_VIDEO_ID, YT_REQUEST_VIDEO_ID_TYPE },
				{ YT_REQUEST_CLICK_TRACKING_PARAMS, YT_REQUEST_CLICK_TRACKING_PARAMS_TYPE },
				{ YT_REQUEST_CLIENT_TYPE, YT_REQUEST_CLIENT_TYPE_TYPE },
				{ YT_REQUEST_USER_AGENT, YT_REQUEST_USER_AGENT_TYPE },
				{ YT_REQUEST_CLIENT_VERSION, YT_REQUEST_CLIENT_VERSION_TYPE },
				{ YT_ENABLE_PLAYBACK_CONTEXT, YT_ENABLE_PLAYBACK_CONTEXT_TYPE },
				{ YT_ENABLE_FALLEN_BACK_MODE, YT_ENABLE_FALLEN_BACK_MODE_TYPE },
				{ YT_ENABLE_PLAYER_TYPE, YT_ENABLE_PLAYER_TYPE_TYPE },
				{ YT_REQUEST_NO_BROWSER_VERSION, YT_REQUEST_NO_BROWSER_VERSION_TYPE },
				{ YT_REQUEST_CUSTOM_PLATFORM, YT_REQUEST_CUSTOM_PLATFORM_TYPE } });

	/* */

	static final String[] EMBED_URLS =
		{ "https://www.google.com/", "https://google.com", "https://www.youtube.com/", };

	public static final String DEFAULT_REQUEST_PLATFORM = "DESKTOP";

	public static boolean isValidObject(final String type, final Object obj) {
		return type != null && !type.isEmpty() && ARGUMENT_CLASSES.containsKey(type) && obj != null && obj.getClass().isInstance(ARGUMENT_CLASSES.get(type));
	}

	public static boolean isValidMap(final Map<String, Object> arguments) {
		return arguments != null;
	}

	public static boolean isEnabled(final Map<String, Object> arguments, final String item) {
		// Apparently we want to check Boolean.class not boolean.class...
		return arguments != null && !arguments.isEmpty() && arguments.containsKey(item) && arguments.get(item).getClass() == Boolean.class
				&& ((boolean) arguments.get(item)) == true;
	}

	public static UniJsonValue makeYTRequestObject(final Map<String, Object> arguments) {
		if (!isValidMap(arguments))
			throw new UnsupportedOperationException("Invalid map provided!");
		final UniJsonValue o = UniJsonValue.object();
		o.add("context", makeYTRequestCtx(arguments));
		if (isEnabled(arguments, YT_ENABLE_PLAYBACK_CONTEXT)) {
			final UniJsonValue playbackContext = UniJsonValue.object(1);
			final UniJsonValue contentPlaybackContext = UniJsonValue.object();
			contentPlaybackContext.populate(new Object[][]
				{
						{ "autoCaptionsDefaultOn", false },
						{ "autonavState", "STATE_NONE" },
						{ "html5Preference", "HTML5_PREF_WANTS" },
						{ "lactMilliseconds", "-1" },
						{ "referer", "https://www.youtube.com/?themeRefresh=1" },
						{ "vis", 0 },
						{ "signatureTimestamp", YouTubeExtractorConstants.getSignatureTimestamp() },
						{ "splay", false } });
			{
				final UniJsonValue watchAmbientModeContext = UniJsonValue.object();
				watchAmbientModeContext.add("hasShownAmbientMode", true);
				watchAmbientModeContext.add("watchAmbientModeEnabled", true);
				contentPlaybackContext.add("watchAmbientModeContext", watchAmbientModeContext);
			}
			final String reference_video = (String) arguments.get(YT_REQUEST_VIDEO_ID);
			if (reference_video != null && !StringsUtil.isBlank(reference_video))
				contentPlaybackContext.add("currentUrl", "/watch?v=" + reference_video);
			playbackContext.add("contentPlaybackContext", contentPlaybackContext);
			o.add("playbackContext", playbackContext);
		}
		return o;
	}

	public static UniJsonValue makeYTRequestCtx(final Map<String, Object> arguments) {
		if (!isValidMap(arguments))
			throw new UnsupportedOperationException("Invalid map provided!");
		return _makeYTRequestCtx(arguments);
	}

	static UniJsonValue _makeYTRequestCtx(final Map<String, Object> arguments) {
		final UniJsonValue context = UniJsonValue.object();
		final UniJsonValue clickTracking = UniJsonValue.object(1);
		final String clickTrackingParams = (String) arguments.get(YT_REQUEST_CLICK_TRACKING_PARAMS);
		if (clickTrackingParams != null && !StringsUtil.isBlank(clickTrackingParams)) {
			clickTracking.add("clickTrackingParams", clickTrackingParams);
		} else {
			clickTracking.add("clickTrackingParams", makeDummyReducedCPN(36));
		}
		context.add("clickTracking", clickTracking);
		final UniJsonValue adSignalsInfo = UniJsonValue.object(1);
		adSignalsInfo.add("params", UniJsonValue.emptyArray());
		context.add("adSignalsInfo", adSignalsInfo);
		final UniJsonValue client = UniJsonValue.object(15);
		final YTIAPIClientType clientType = (YTIAPIClientType) arguments.getOrDefault(YT_REQUEST_CLIENT_TYPE, YTIAPIClientType.WEB);
		final String userAgent = (String) arguments.getOrDefault(YT_REQUEST_USER_AGENT, URLUtils.STANDARD_USERAGENT);
		final String CLIENT_VERSION = (String) arguments.getOrDefault(YT_REQUEST_CLIENT_VERSION, YouTubeExtractorConstants.CLIENT_VERSION);
		final boolean disableBrowserVersion = isEnabled(arguments, YT_REQUEST_NO_BROWSER_VERSION);
		final String platform = (String) arguments.getOrDefault(YT_REQUEST_CUSTOM_PLATFORM, DEFAULT_REQUEST_PLATFORM);
		client.populate(new Object[][]
			{
					{ "acceptHeader",
							"text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" },
					{ "clientFormFactor", "UNKNOWN_FORM_FACTOR" },
					{ "clientName", clientType.get(YTIAPIClientEnums.ClientName) },
					{ "clientVersion", CLIENT_VERSION },
					{ "connectionType", "CONN_WIFI" },
					{ "deviceMake", "" },
					{ "deviceModel", "" },
					/* Apparently setting it to EN is not supported, fair enough. */
					{ "gl", "US" },
					{ "hl", YT_DEFAULT_LANG_CODE },
					{ "osName", clientType.get(YTIAPIClientEnums.OSName) },
					{ "osVersion", "" },
					{ "platform", platform },
					{ "screenDensityFloat", 1 },
					{ "screenPixelDensity", 1 },
					{ "userAgent", (userAgent != null ? userAgent : URLUtils.STANDARD_USERAGENT) },
					{ "utcOffsetMinutes", 60 },
					{ "visitorData", YouTubeExtractorConstants.VISITOR_DATA }, },
				false);
		if (!disableBrowserVersion) {
			client.add("browserName", "Chrome");
			client.add("browserVersion", URLUtils.USER_AGENT_VERSION);
		}
		if (clientType.contains(YTIAPIClientEnums.IsEmbeddedContext)) {
			client.add("clientScreen", "EMBED");
		}
		if (isEnabled(arguments, YT_ENABLE_PLAYER_TYPE)) {
			client.add("playerType", "UNIPLAYER");
		}
		if (clientType.contains(YTIAPIClientEnums.EnableBaltimore)) {
			final BaltimoreUserAgent ua = BaltimoreUserAgent.fromId(clientType.get(YTIAPIClientEnums.EnableBaltimore));

			client.add("package_name", ua.getPackageName());
			client.add("gmp_version", 87011L);
			client.add("app_version", ua.getPackageVersion());
			client.add("app_version_int", ua.getPackageVersionCode());
			client.add("dynamite_version", BaltimoreUserAgent.BALTIMORE_DYNAMITE_VERSION);
		}
		final String reference_video = (String) arguments.get(YT_REQUEST_VIDEO_ID);
		if (reference_video != null && !StringsUtil.isBlank(reference_video)) {
			client.add("originalUrl", "https://www.youtube.com/watch?v=" + reference_video);
		} else {
			client.add("originalUrl", "https://www.youtube.com/");
		}
		if (isEnabled(arguments, YT_ENABLE_FALLEN_BACK_MODE)) {
			client.add("clientScreen", "EMBED");
			final UniJsonValue tp = UniJsonValue.object();
			tp.add("embedUrl", ArrayUtils.random(EMBED_URLS));
			context.add("thirdParty", tp);
		}
		context.add("client", client);
		final UniJsonValue request = UniJsonValue.object();
		request.add("consistencyTokenJars", UniJsonValue.emptyArray());
		request.add("internalExperimentFlags", UniJsonValue.emptyArray());
		request.add("useSsl", true);
		context.add("request", request);
		final UniJsonValue user = UniJsonValue.object(1);
		user.add("lockedSafetyMode", false);
		context.add("user", user);
		return context;
	}
}
